import axios from 'axios'
import store from '@/store'
import { Message } from 'element-ui'
import router from '@/router'
import qs from 'qs'

interface RequestItem {
  (): void
}

function redirectLogin () {
  router.push({
    name: 'login',
    query: {
      redirect: router.currentRoute.fullPath
    }
  })
}

function refreshToken () {
  // 创建新的实例是为防止获取 refresh_token 失败造成重复请求的问题
  // 新创建的实例和 request 没有关联
  return axios.create()({
    method: 'POST',
    url: '/front/user/refresh_token',
    data: qs.stringify({
      // refresh_token 只能使用1次
      refreshtoken: store.state.user.refresh_token
    })
  })
}

const request = axios.create({
  // 配置选项
  // baseURL,
  // timeout
})

// 请求拦截器
request.interceptors.request.use(
  config => {
    const { user } = store.state
    if (user && user.access_token) {
      config.headers.Authorization = user.access_token
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)
// 响应拦截器
let isRefreshing = false // 控制刷新 token
let requests: RequestItem[] = [] // 存储刷新 token 期间过来的 401 请求
request.interceptors.response.use(
  response => {
    return response
  },
  async error => {
    if (error.response) { // 请求发出去且有响应，但是超出 2xx 的范围
      const { status } = error.response
      // 400 客户端请求参数错误
      // 401 一般是表示未授权
      // 403 一般是表示没有权限
      // 404
      // 500
      switch (status) {
        case 400:
          Message.error('请求参数错误')
          break
        case 401:
          // token 无效 （没有提供token、token 过期、token 无效）
          // 如果有 refresh_token 则尝试使用 refresh_token 获取新的 access_token
          // 没有 refresh_token 跳转到登录页
          if (!store.state.user) {
            redirectLogin()
            return Promise.reject(error)
          }
          // 尝试获取新的 token
          if (!isRefreshing) {
            isRefreshing = true // 开启刷新状态
            return refreshToken()
              .then(res => {
                if (!res.data.success) {
                  throw new Error('刷新 Token 失败')
                }
                // 获取成功则将本次失败的请求重新发出去
                store.commit('SET_USER', res.data.content)
                // 把 requests 队列中的请求重新发出去
                requests.forEach(cb => cb())
                // 重置 requests 数组
                requests = []
                return request(error.config)
              })
              .catch(error => {
                // 清楚当前用户登录状态
                store.commit('SET_USER', null)
                // 失败跳转到登录页面获取新的 token
                redirectLogin()
                return Promise.reject(error)
              })
              .finally(() => {
                isRefreshing = false // 重值刷新状态
              })
          }
          // 刷新状态下 将请求挂起放到 requests 中
          return new Promise(resolve => {
            requests.push(() => {
              resolve(request(error.config))
            })
          })
          break
        case 403:
          Message.error('没有权限，请联系管理员')
          break
        case 404:
          Message.error('请求资源不存在')
          break
        default:
          Message.error('服务端错误，请联系管理员')
          break
      }
    } else if (error.request) { // 请求发出去但是没有响应
      Message.error('请求超时，请刷新重试')
    } else { // 在设置请求时发生了一些事情，触发了一个错误
      Message.error(`请求失败：${error.message}`)
    }
    return Promise.reject(error)
  }
)

export default request
